// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CommandBarButtonExtensions.cs" company="Roman '4ux-nbIx' Novitsky">
//   Copyright (c) Roman '4ux-nbIx' Novitsky 2010
// </copyright>
// <summary>
//   Defines  class extension methods.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace TaskbarButtons
{
  #region Namespace Imports

  using System;
  using System.Collections.Generic;
  using System.Diagnostics.CodeAnalysis;
  using System.Drawing;
  using System.Linq;
  using System.Reflection;
  using System.Runtime.InteropServices;
  using System.Windows;
  using System.Windows.Forms;
  using System.Windows.Media;
  using System.Windows.Media.Imaging;

  using EnvDTE;

  using Microsoft.VisualStudio.CommandBars;

  using Color = System.Drawing.Color;
  using PixelFormat = System.Drawing.Imaging.PixelFormat;

  #endregion


  /// <summary>
  /// Defines <see cref="CommandBarButton"/> class extension methods.
  /// </summary>
  internal static class CommandBarButtonExtensions
  {
    private static readonly Dictionary<int, Icon> _iconsCache = new Dictionary<int, Icon>();

    #region Public Methods

    /// <summary>
    /// Gets the caption.
    /// </summary>
    /// <param name="command">
    /// The command.
    /// </param>
    /// <returns>
    /// The caption.
    /// </returns>
    public static string GetCaption(this CommandBarButton command)
    {
      return command.Caption.Replace("&", string.Empty);
    }


    /// <summary>
    /// Gets the icon.
    /// </summary>
    /// <param name="command">
    /// The command bar button.
    /// </param>
    /// <returns>
    /// The command bar button icon.
    /// </returns>
    [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Don't care.")]
    public static Icon GetIcon(this CommandBarButton command)
    {
      if (command == null)
      {
        return null;
      }

      try
      {
        if (command.Picture == null)
        {
          return null;
        }

        if (_iconsCache.ContainsKey(command.Picture.Handle))
        {
          return _iconsCache[command.Picture.Handle];
        }

        var bitmap = ImageFromIPicture(command.Picture) as Bitmap;

        var bitmapSource = BitmapSourceFromBitmap(bitmap);

        bitmap = BitmapFromBitmapSource(bitmapSource);

        var intPtr = bitmap.GetHicon();

        var icon = Icon.FromHandle(intPtr);

        _iconsCache.Add(command.Picture.Handle, icon);

        return icon;
      }
      catch
      {
        return null;
      }
    }

    #endregion


    #region Methods

    /// <summary>
    /// Gets the icon.
    /// </summary>
    /// <param name="applicationObject">
    /// The application object.
    /// </param>
    /// <param name="path">
    /// The menu item path.
    /// </param>
    /// <returns>
    /// The menu item icon.
    /// </returns>
    [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Don't care.")]
    [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Don't care.")]
    internal static CommandBarButton GetCommandBarButton(this DTE applicationObject, string path)
    {
      try
      {
        var pathElements = path.Split(new[] { "\\" }, StringSplitOptions.RemoveEmptyEntries).ToList();

        var commandBars = (CommandBars)applicationObject.CommandBars;

        int count = commandBars.Count;
        for (int i = 1; i < count; i++)
        {
          CommandBar bar = commandBars[i];

          if (bar.Name.Replace("&", string.Empty) != pathElements[0])
          {
            continue;
          }

          return GetCommandBarButton(pathElements, 1, bar);
        }
      }
      catch (Exception)
      {
        return null;
      }

      return null;
    }


    /// <summary>
    /// Bitmaps from bitmap source.
    /// </summary>
    /// <param name="bitmapSource">
    /// The bitmap source.
    /// </param>
    /// <returns>
    /// Returns an instance of <see cref="System.Drawing.Bitmap"/>.
    /// </returns>
    private static unsafe Bitmap BitmapFromBitmapSource(BitmapSource bitmapSource)
    {
      PixelFormat undefined;
      if (bitmapSource.Format == PixelFormats.Bgra32)
      {
        undefined = PixelFormat.Format32bppArgb;
      }
      else
      {
        if (bitmapSource.Format != PixelFormats.Bgr32)
        {
          throw new ArgumentException("The supplied image is not a bitmap, or is not of the supported format.");
        }

        undefined = PixelFormat.Format32bppRgb;
      }

      int pixelWidth = bitmapSource.PixelWidth;
      int pixelHeight = bitmapSource.PixelHeight;
      int stride = pixelWidth * (bitmapSource.Format.BitsPerPixel / 8);
      var pixels = new byte[stride * pixelHeight];
      bitmapSource.CopyPixels(pixels, stride, 0);
      fixed (byte* numRef = pixels)
      {
        return new Bitmap(pixelWidth, pixelHeight, stride, undefined, new IntPtr(numRef));
      }
    }


    /// <summary>
    /// Bitmaps the source from bitmap.
    /// </summary>
    /// <param name="bitmap">
    /// The bitmap.
    /// </param>
    /// <returns>
    /// Returns an instance of <see cref="System.Windows.Media.Imaging.BitmapSource"/>.
    /// </returns>
    private static BitmapSource BitmapSourceFromBitmap(Bitmap bitmap)
    {
      return BitmapSourceSectionFromBitmap(bitmap, new Int32Rect(0, 0, bitmap.Width, bitmap.Height));
    }


    /// <summary>
    /// Bitmaps the source section from bitmap.
    /// </summary>
    /// <param name="bitmap">
    /// The bitmap.
    /// </param>
    /// <param name="sectionRectangle">
    /// The section rectangle.
    /// </param>
    /// <returns>
    /// Returns an instance of <see cref="System.Windows.Media.Imaging.BitmapSource"/>.
    /// </returns>
    private static BitmapSource BitmapSourceSectionFromBitmap(Bitmap bitmap, Int32Rect sectionRectangle)
    {
      BitmapSource source;

      using (var locker = new BitmapLocker(bitmap))
      {
        int length = Math.Abs(locker.BitmapData.Stride);
        int num2 = bitmap.Height * length;
        var destination = new byte[num2];
        if (locker.BitmapData.Stride > 0)
        {
          Marshal.Copy(locker.BitmapData.Scan0, destination, 0, num2);
        }
        else
        {
          IntPtr ptr = locker.BitmapData.Scan0;
          for (int i = 0; i < bitmap.Height; i++)
          {
            Marshal.Copy(ptr, destination, i * length, length);
            ptr -= length;
          }
        }

        PixelFormat pixelFormat = bitmap.PixelFormat;
        if (pixelFormat != PixelFormat.Format24bppRgb)
        {
          if (pixelFormat == PixelFormat.Format32bppRgb)
          {
            goto Label00B6;
          }

          if (pixelFormat == PixelFormat.Format1bppIndexed)
          {
            goto Label00C3;
          }
        }
        else
        {
          destination = ExtractPixelSection(destination, 24, length, sectionRectangle);
        }

        goto Label00D6;
        Label00B6:
        destination = ExtractPixelSection(destination, 32, length, sectionRectangle);
        goto Label00D6;
        Label00C3:
        destination = ExtractPixelSection(destination, 1, length, sectionRectangle);
        length = bitmap.Width * 4;
        Label00D6:
        source = BitmapSource.Create(
          bitmap.Width, bitmap.Height, 96.0, 96.0, PixelFormats.Bgra32, null, destination, length);
      }

      return source;
    }


    private static byte[] ExtractPixelSection(
      IList<byte> sourcePixels, 
      int sourceBitsPerPixel, 
      int sourceStride, 
      Int32Rect sectionRect, 
      Color[] transparentColors)
    {
      int num = (sectionRect.Width * sectionRect.Height) * 4;
      if (sourceBitsPerPixel == 1)
      {
        var buffer = new byte[num];
        for (int j = 0; j < sectionRect.Height; j++)
        {
          for (int k = 0; k < sectionRect.Width; k++)
          {
            int index = (sectionRect.X + ((j + sectionRect.Y) * sourceStride)) + (k / 8);
            int num5 = (k * 4) + (j * (sectionRect.Width * 4));
            var num6 = (byte)(1 << (7 - (k % 8)));
            bool flag = (sourcePixels[index] & num6) == num6;
            buffer[num5] = flag ? ((byte)255) : ((byte)0);
            buffer[num5 + 1] = flag ? ((byte)255) : ((byte)0);
            buffer[num5 + 2] = flag ? ((byte)255) : ((byte)0);
            buffer[num5 + 3] = flag ? ((byte)255) : ((byte)0);
          }
        }

        return buffer;
      }

      int num7 = sourceBitsPerPixel / 8;
      bool flag2 = false;
      if (sourceBitsPerPixel == 32)
      {
        flag2 = true;
        for (int m = 0; m < sourcePixels.Count; m += 4)
        {
          if (sourcePixels[m + 3] == 0)
          {
            continue;
          }

          flag2 = false;
          break;
        }
      }

      var buffer2 = new byte[num];
      for (int i = 0; i < sectionRect.Height; i++)
      {
        for (int n = 0; n < sectionRect.Width; n++)
        {
          int num12 = ((n + sectionRect.X) * num7) + ((i + sectionRect.Y) * sourceStride);
          int num13 = (n * 4) + (i * (sectionRect.Width * 4));
          byte blue = sourcePixels[num12];
          byte green = sourcePixels[num12 + 1];
          byte red = sourcePixels[num12 + 2];
          byte num17 = (sourceBitsPerPixel == 32) ? sourcePixels[num12 + 3] : ((byte)255);
          Color c = Color.FromArgb(red, green, blue);
          if (Array.FindIndex(transparentColors, transparentColor => c == transparentColor) != -1)
          {
            num17 = 0;
          }
          else if (flag2)
          {
            num17 = 255;
          }

          if (num17 == 0)
          {
            blue = 0;
            green = 0;
            red = 0;
          }

          buffer2[num13] = blue;
          buffer2[num13 + 1] = green;
          buffer2[num13 + 2] = red;
          buffer2[num13 + 3] = num17;
        }
      }

      return buffer2;
    }


    private static byte[] ExtractPixelSection(
      IList<byte> sourcePixels, int sourceBitsPerPixel, int sourceStride, Int32Rect sectionRect)
    {
      var transparentColors = new[] { Color.FromArgb(0, 254, 0), Color.FromArgb(255, 0, 255) };
      return ExtractPixelSection(sourcePixels, sourceBitsPerPixel, sourceStride, sectionRect, transparentColors);
    }


    [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Don't care")]
    private static CommandBarButton GetCommandBarButton(
      IList<string> pathElements, int pathElementIndex, CommandBar bar)
    {
      int count = bar.Controls.Count;

      for (int i = 1; i < count; i++)
      {
        CommandBarControl control = bar.Controls[i];

        if (control.Caption.Replace("&", string.Empty) != pathElements[pathElementIndex])
        {
          continue;
        }

        var popup = control as CommandBarPopup;
        var command = control as CommandBarButton;

        if (popup != null)
        {
          pathElementIndex++;

          return GetCommandBarButton(pathElements, pathElementIndex, popup.CommandBar);
        }

        return command;
      }

      return null;
    }


    /// <summary>
    /// Images from I picture.
    /// </summary>
    /// <param name="picture">
    /// The picture.
    /// </param>
    /// <returns>
    /// Returns an instance of <see cref="System.Drawing.Image"/>.
    /// </returns>
    [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Don't care.")]
    private static Image ImageFromIPicture(object picture)
    {
      try
      {
        var type = typeof(AxHost);

        var methodInfo = type.GetMethod("GetPictureFromIPicture", BindingFlags.Static | BindingFlags.NonPublic);

        return (Image)methodInfo.Invoke(null, new[] { picture });
      }
      catch
      {
        return null;
      }
    }

    #endregion
  }
}